package com.linh.trade;

import java.util.ArrayList;
import java.util.List;

import javax.management.RuntimeErrorException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.linh.trade.Trade.Direction;
import com.linh.trade.algo.Move;
import com.linh.trade.algo.Move.Action;
import com.linh.trade.algo.Strategy;
import com.linh.trade.data.NewPointListener;
import com.linh.trade.data.Point;
import com.linh.trade.valuation.Valuator;

public class Trader implements NewPointListener {

	private static final Logger LOG = LoggerFactory.getLogger(Trader.class);

	private double maxLoss = 0.0005;

	private double ripProfit = 0.0008;

	private double maxAmount = 20000;

	private double minAmount = 10000;
	private double hurdle = Math.pow(10, -4);
	private Strategy strategy;

	private Operator operator;

	private Valuator valuator;

	private List<Trade> openPositions;

	private List<TraderActionListener> listeners;

	double magnitudeSum = 0;
	Point lastPoint;

	int counter = 0;

	public Trader(Strategy strategy, Operator operator, Valuator valuator,
			double maxAmount, double minAmount, double hurdle, double maxLoss,
			double ripProfit) {
		super();
		this.strategy = strategy;
		this.operator = operator;
		this.valuator = valuator;
		this.openPositions = new ArrayList<Trade>();
		this.listeners = new ArrayList<TraderActionListener>();
		this.maxAmount = maxAmount;
		this.minAmount = minAmount;
		this.hurdle = hurdle;
		this.maxLoss = maxLoss;
		this.ripProfit = ripProfit;
	}

	public void trade(Point point) {

		Move move = strategy.nextMove(point);
		LOG.info("Next move:" + move);
		Action action = move.getAction();
		double magnitude = move.getMagnitude();

		double absMag = Math.abs(magnitude);
		magnitudeSum += absMag;
		double normalizedMag = absMag / hurdle;
		double amount = Math.ceil(normalizedMag * 10) * 1000;
		if (amount > maxAmount) {
			amount = maxAmount;
		}
		if (amount < minAmount) {
			amount = minAmount;
		}

		if (action != Action.DO_NOTHING) {
			// check budget
			double currentBudget = operator.getBudget();
			while (currentBudget < amount&&openPositions.size()>0) {
				cashOut(point);
				currentBudget = operator.getBudget();
			}
			if(currentBudget<amount){
				throw new RuntimeException("Loss All the Money!!!");
			}

			if (action == Action.GO_LONG) {
				Trade trade = operator.goLong(point, amount);
				fireGoLongAction(trade);
				openPositions.add(trade);
			} else if (action == Action.GO_SHORT) {
				Trade trade = operator.goShort(point, amount);
				fireGoShortAction(trade);
				openPositions.add(trade);
			}
		}

		// discipline
		closePosition(point, move);
		//done with the point
		point.setInView(false);

	}

	public void fireGoLongAction(Trade trade) {
		for (TraderActionListener listener : listeners) {
			listener.onGoingLong(trade, operator.getBudget());
		}
	}

	public void fireGoShortAction(Trade trade) {
		for (TraderActionListener listener : listeners) {
			listener.onGoingShort(trade, operator.getBudget());
		}
	}

	public void firePositionCloseAction(Trade trade) {
		for (TraderActionListener listener : listeners) {
			listener.onPositionClosed(trade, operator.getBudget());
		}
	}

	private void cashOut(Point point) {
		// pick the worst trade
		double pnl = Double.MAX_VALUE;
		Trade worst = null;
		for (Trade trade : openPositions) {
			double tradePnl = valuator.unrealizedPnL(trade, point);
			if (tradePnl < pnl) {
				worst = trade;
			}
		}

		// close it
		operator.closePosition(worst, point);
		firePositionCloseAction(worst);
		openPositions.remove(worst);
	}

	/**
	 * @param point
	 * @param move
	 */
	private void closePosition(Point point, Move move) {

		double signal = move.getMagnitude();
		for (int i = 0; i < openPositions.size(); i++) {
			Trade trade = openPositions.get(i);
			// momentum is reversed
			if ((signal > 0 && trade.getDirection() == Direction.SHORT)
					|| (signal < 0 && trade.getDirection() == Direction.LONG)) {
				double pnl = valuator.unrealizedPnL(trade, point);
				double percentage = Math.abs(pnl) / trade.getAmount();
				if ((pnl < 0 && percentage >= maxLoss)
						|| (pnl > 0 && percentage >= ripProfit)) {
					operator.closePosition(trade, point);
					firePositionCloseAction(trade);
					openPositions.remove(i);
					i--;
				}

			}
		}

	}

	private void closeAllPositions(Point point) {
		for (int i = 0; i < openPositions.size(); i++) {
			Trade trade = openPositions.get(i);

			operator.closePosition(trade, point);
			firePositionCloseAction(trade);
			openPositions.remove(i);
			i--;

		}
	}

	public void addTraderActionListener(TraderActionListener listener) {
		listeners.add(listener);
	}

	public void newPoint(Point p) {
		lastPoint = p;
		trade(p);
		counter++;

	}

	public void endOfData() {
		LOG.info("Avg magnitude " + magnitudeSum / counter);

		closeAllPositions(lastPoint);
		int noOfPoints = counter;
		int noOfTrades = valuator.getCurrentTrades().size();
		double chance = (double) noOfTrades / noOfPoints;
		LOG.info("Total number of Points " + noOfPoints);
		LOG.info("Total number of Trades " + noOfTrades);
		LOG.info("Take " + chance + " % chance");
		double pnl = valuator.realizedPnL();
		LOG.info("PnL= " + (operator.budget - operator.initialBudget));
		LOG.info("Valuated PnL= " + pnl);
		double ror = pnl / operator.budget * 100;
		LOG.info("RoR=" + ror + "%");

	}
	
	
}
